package de.core.gameplay.level;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import de.core.gameplay.actor.AbstractActor;

public class AbstractLevel implements Level {

    private Image background;
    private int width;
    private int height;

    private List<AbstractActor> actors;
    private List<AbstractActor> actorsToAdd;
    private List<AbstractActor> actorsToRemove;


    protected AbstractLevel(Image aBackground, int aWidth, int aHeight) {
        background = aBackground;
        width = aWidth;
        height = aHeight;

        actors = new ArrayList<AbstractActor>();
        actorsToAdd = new ArrayList<AbstractActor>();
        actorsToRemove = new ArrayList<AbstractActor>();
    }

    @Override
    public synchronized void addActor(AbstractActor aActor) {
        aActor.setLevel(this);
        actorsToAdd.add(aActor);
    }

    @Override
    public synchronized void removeActor(AbstractActor aActor) {
        actorsToRemove.add(aActor);
    }

    @Override
    public void updateGameState() {
        actors.addAll(actorsToAdd);
        actors.removeAll(actorsToRemove);
        actorsToAdd.clear();
        actorsToRemove.clear();

        for (AbstractActor actor : getActors()) {
            actor.updateGameState();
        }
    }

    @Override
    public void render(Graphics2D aRenderGraphics, float aInterpolation) {
        if (background != null) {
            aRenderGraphics.drawImage(background, 0, 0, null);
        }

        for (AbstractActor actor : actors) {
            actor.render(aRenderGraphics, aInterpolation);
        }
    }

    @Override
    public List<AbstractActor> getActors() {
        return actors;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public List<AbstractActor> getActorsAt(int aX, int aY) {
        return getActorsAt(new Point2D.Double(aX, aY));
    }

    @Override
    public List<AbstractActor> getActorsAt(Point2D aPoint) {
        return getActorsAt(aPoint, AbstractActor.class);
    }

    @Override
    public <_Class extends AbstractActor> List<_Class> getActorsAt(Point2D aPoint, Class<_Class> aClass) {
        List<_Class> actors = new ArrayList<_Class>();
        for (AbstractActor actor : getActors()) {
            if (!aClass.isAssignableFrom(actor.getClass())) {
                continue;
            }

            if (isActorOnPoint(actor, aPoint)) {
                actors.add((_Class) actor);
            }
        }
        return actors;
    }

    private boolean isActorOnPoint(AbstractActor aActor, Point2D aPoint) {
        double minX = aActor.getLocation().getX() - aActor.getImage().getWidth() / 2;
        double maxX = aActor.getLocation().getX() + aActor.getImage().getWidth() / 2;
        double minY = aActor.getLocation().getY() - aActor.getImage().getHeight() / 2;
        double maxY = aActor.getLocation().getY() + aActor.getImage().getHeight() / 2;

        return aPoint.getX() >= minX && aPoint.getX() <= maxX
            && aPoint.getY() >= minY && aPoint.getY() <= maxY;
    }

    @Override
    public List<AbstractActor> getActorsIntercectingWith(AbstractActor aActor) {
        return getActorsIntercectingWith(aActor, AbstractActor.class);
    }

    @Override
    public <_Class extends AbstractActor> List<_Class> getActorsIntercectingWith(AbstractActor aActor,
                                                                                 Class<_Class> aClass) {
        List<_Class> actors = new ArrayList<_Class>();
        for (AbstractActor actor : getActors()) {
            if (!aClass.isAssignableFrom(actor.getClass())) {
                continue;
            }

            if (aActor.intersects(actor)) {
                actors.add((_Class) actor);
            }
        }
        return actors;
    }
}
